package com.moleq.remote.rpt;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.Method;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;

import com.moleq.util.ServiceUtil;

public class RemoteReportServer {

	private int mPort;
	private ServerSocket ss = null;
	private boolean started = false;
	private List<WindowClient> winClients = new ArrayList<WindowClient>();
	private int bufferSize = 1024;

	private SocketRequest svrRequest = null;
	private SocketResponse svrResponse = null;

	public void start(int port) {
		mPort = port;
		new Thread() {

			public void run() {
				try {
					ss = new ServerSocket(mPort);
					started = true;
					System.out.println("Remote report server is started.");
				} catch (BindException e) {
					e.printStackTrace();
				} catch (IOException e) {
					// TODO: handle exception
					e.printStackTrace();
				}

				try {
					while (started) {
						Socket s = ss.accept();
						WindowClient c = new WindowClient(s);
						new Thread(c).start();
						winClients.add(c);
					}
				} catch (IOException e) {
					// TODO: handle exception
				} finally {

					release();

					try {
						Thread.sleep(5000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					restart(mPort);
				}
			}
		}.start();

	}

	public void restart(int port) {
		new RemoteReportServer().start(port);
	}

	public void release() {
		System.out.println("Remote report server  is released.");
		try {
			if (ss != null) {
				ss.close();
			}
			started = false;
		} catch (IOException e2) {
			e2.printStackTrace();
		}

	}

	class WindowClient implements Runnable {

		private Socket s = null;
		private InputStream is = null;
		private OutputStream os = null;
		private boolean bConnected = false;
		private String sendmsg = null;

		public WindowClient(Socket s) {
			this.s = s;
			try {
				is = s.getInputStream();
				os = s.getOutputStream();
				bConnected = true;
				System.out.println(s.getInetAddress() + "  is connected.");
			} catch (Exception e) {
				// TODO: handle exception
				e.printStackTrace();
			}
		}

		public void sendMessage(String message) {
			try {
				OutputStreamWriter osw = new OutputStreamWriter(os);
				BufferedWriter writer = new BufferedWriter(osw);

				writer.write(message);
				writer.flush();
			} catch (IOException e) {
				winClients.remove(this);
				e.printStackTrace();
			}
		}

		public void release() {
			System.out.println(s.getInetAddress() + "  is released.");
			try {
				if (is != null) {
					is.close();
				}
				if (os != null) {
					os.close();
				}
				if (s != null) {
					s.close();
				}
				bConnected = false;
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		@Override
		public void run() {
			// TODO Auto-generated method stub

			while (bConnected) {
				if (s == null || !s.isConnected()) {
					continue;
				}

				BufferedReader reader;
				try {

					byte[] buffer = new byte[bufferSize];

					String recBuffer = "";
					int readCount = 0;
					while ((readCount = is.read(buffer, 0, bufferSize)) > 0) {

						recBuffer += new String(buffer, 0, readCount);
						if (readCount < bufferSize) {
							System.out.println("receive end ->" + recBuffer);
							svrRequest = JsonConvertor.toSocketRequest(recBuffer);
							if (svrRequest == null || svrRequest.equals("")) {
								svrResponse = new SocketResponse();
							} else {
								svrResponse = process(svrRequest);
							}
							if (svrResponse != null) {
								svrResponse.CreateDateTime = ServiceUtil.getCurrentDateTime();//new java.util.Date();
								svrResponse.FromIpAddress = ServiceUtil.getLocalIpAddress();
								this.sendMessage(JsonConvertor.socketResponse2String(svrResponse));
							}
						}
					}

				} catch (SocketException e) {
					winClients.remove(this);
				} catch (EOFException e) {
					winClients.remove(this);
				} catch (IOException e) {
					e.printStackTrace();
				} finally {
					release();
				}
			}

		}

		public SocketResponse process(SocketRequest request) {

			SocketResponse response = new SocketResponse();

			String functionName = request.ClassName;
			String methodName = request.MethodName;

			int paramCount = request.Params.size();
			Class[] paramTypes = new Class[paramCount];
			Object[] params = new Object[paramCount];
			for (int i = 0; i < paramCount; i++) {
				paramTypes[i] = request.Params.get(i).getClass();
				params[i] = request.Params.get(i);
			}

			// String result = "<Argument type=\"%s\" value=\"%s\"/>";
			Object result = null;
			try {
				Class c = Class.forName("com.moleq.remote.rpt." + functionName + "ResponseManager");
				// manager.process(command);

				Object newClass = c.newInstance();

				Method method = PrivateUtil.getMethod(c, methodName, paramTypes);
				// System.out.println("return -> " + method.getReturnType());
				if (method.getReturnType().toString().equals("void")) {
					PrivateUtil.invoke(newClass, methodName, paramTypes, params);
					result = null;
				} else {
					Object object = PrivateUtil.invoke(newClass, methodName, paramTypes, params);
					result = object;
				}

				// this.sendMessage(result);
				// this.sendMessage(manager.getResponse());
				response.HasError = false;
				response.ResponseResult = result;

			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				// this.sendMessage(""+ e.getMessage());
				response.HasError = true;
				response.ErrorMsg = e.toString();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				// this.sendMessage(""+ e.getMessage());
				response.HasError = true;
				response.ErrorMsg = e.toString();
			} catch (InstantiationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				// this.sendMessage(""+ e.getMessage());
				response.HasError = true;
				response.ErrorMsg = e.toString();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				// this.sendMessage(""+ e.getMessage());
				response.HasError = true;
				response.ErrorMsg = e.toString();
			}

			return response;
		}
	}
}
